Add Geogrid-Viewer binary file reader
authorRalf Horstmann <ralf+github@ackstorm.de>
Thu, 7 Jan 2016 23:53:34 +0000 (00:53 +0100)
committerRalf Horstmann <ralf+github@ackstorm.de>
Fri, 15 Jan 2016 23:20:59 +0000 (00:20 +0100)
Supports file format version 2.0, 3.0 and 4.0

Makefile.in
ggv_bin.cc [new file with mode: 0644]
reference/ggv_bin-sample-v3.gpx [new file with mode: 0644]
reference/ggv_bin-sample-v3.ovl [new file with mode: 0644]
testo.d/ggv_bin.test [new file with mode: 0644]
vecs.cc
xmldoc/formats/ggv_bin.xml [new file with mode: 0644]

index 11532aa415f27cbf5513d1540bd931cedca4ff21..87654aebfbc7d8780f3d9099719f990f84e6942a 100644 (file)
@@ -81,7 +81,7 @@ ALL_FMTS=$(MINIMAL_FMTS) gtm.o gpsutil.o  \
        vpl.o teletype.o jogmap.o bushnell.o bushnell_trl.o wintec_tes.o \
        subrip.o garmin_xt.o garmin_fit.o lowranceusr4.o \
        mtk_locus.o googledir.o mapbar_track.o f90g_track.o mapfactor.o energympro.o \
-       mynav.o
+       mynav.o ggv_bin.o
 
 FMTS=@FMTS@
 
@@ -578,6 +578,8 @@ gdb.o: gdb.cc defs.h config.h queue.h zlib/zlib.h zlib/zconf.h gbfile.h \
   garmin_tables.h grtcirc.h
 geo.o: geo.cc defs.h config.h queue.h zlib/zlib.h zlib/zconf.h gbfile.h \
   cet.h inifile.h session.h src/core/datetime.h src/core/file.h
+ggv_bin.o: ggv_bin.cc defs.h config.h queue.h zlib/zlib.h zlib/zconf.h \
+  gbfile.h cet.h inifile.h session.h src/core/datetime.h
 ggv_log.o: ggv_log.cc defs.h config.h queue.h zlib/zlib.h zlib/zconf.h \
   gbfile.h cet.h inifile.h session.h src/core/datetime.h grtcirc.h \
   jeeps/gpsmath.h jeeps/gpsport.h
diff --git a/ggv_bin.cc b/ggv_bin.cc
new file mode 100644 (file)
index 0000000..1d64f4d
--- /dev/null
@@ -0,0 +1,534 @@
+/*
+
+    Handle Geogrid-Viewer binary overlay file format (.ovl)
+
+    Copyright (C) 2016 Ralf Horstmann <ralf@ackstorm.de>
+    Copyright (C) 2016 Robert Lipe, robertlipe+source@gpsbabel.org
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
+
+*/
+
+#include "defs.h"
+#include <QtCore/QtEndian>
+#include <QtCore/QByteArray>
+#include <QtCore/QDebug>
+#include <QtCore/QFile>
+
+#define MYNAME "ggv_bin"
+
+static QString read_fname;
+
+/***************************************************************************
+ *           local helper functions                                        *
+ ***************************************************************************/
+
+static void
+ggv_bin_read_bytes(QFile& file, QByteArray& buf, qint64 size, const char* descr = NULL)
+{
+  if (size > file.size())
+    fatal(MYNAME ": Read size error (%s)\n", descr ? descr : "");
+  buf = file.read(size);
+  if (buf.size() < size)
+    fatal(MYNAME ": Read error (%s)\n", descr ? descr : "");
+}
+
+static quint16
+ggv_bin_read16(QFile& file, const char* descr = NULL)
+{
+  QByteArray buf;
+  quint16 res;
+
+  ggv_bin_read_bytes(file, buf, 2, descr);
+  res = qFromLittleEndian<quint16>((const uchar*)buf.constData());
+  if (global_opts.debug_level > 1)
+    qDebug("ovl: %-15s %5u (0x%04x)", descr, res, res);
+  return res;
+}
+
+static quint32
+ggv_bin_read32(QFile& file, const char* descr = NULL)
+{
+  QByteArray buf;
+  quint32 res;
+
+  ggv_bin_read_bytes(file, buf, 4, descr);
+  res = qFromLittleEndian<quint32>((const uchar*)buf.constData());
+  if (global_opts.debug_level > 1) {
+    if ((res & 0xFFFF0000) == 0) 
+      qDebug("ovl: %-15s %5u (0x%08x)", descr, res, res);
+    else
+      qDebug("ovl: %-15s       (0x%08x)", descr, res);
+  }
+  return res;
+}
+
+static void
+ggv_bin_read_text16(QFile& file, QByteArray& buf, const char* descr = NULL)
+{
+  quint16 len;
+  
+  len = ggv_bin_read16(file, descr);
+  ggv_bin_read_bytes(file, buf, len, descr);
+  buf[len] = 0;
+  if (global_opts.debug_level > 1)
+    qDebug() << "ovl: text =" << QString::fromLatin1(buf.constData()).simplified();
+}
+
+static void
+ggv_bin_read_text32(QFile& file, QByteArray& buf, const char* descr = NULL)
+{
+  quint32 len;
+  
+  len = ggv_bin_read32(file, descr);
+  ggv_bin_read_bytes(file, buf, len, descr);
+  buf[len] = 0;
+  if (global_opts.debug_level > 1)
+    qDebug() << "ovl: text =" << QString::fromLatin1(buf.constData()).simplified();
+}
+
+static double
+ggv_bin_read_double(QFile& file, const char* descr = NULL)
+{
+  QByteArray buf;
+  quint64 tmp;
+  const double *res;
+
+  ggv_bin_read_bytes(file, buf, sizeof(double), descr);
+  tmp = qFromLittleEndian<quint64>((const uchar*)buf.constData());
+  res = reinterpret_cast<const double*>(&tmp);
+  return *res;
+}
+
+/***************************************************************************
+ *            OVL Version 2.0                                              *
+ ***************************************************************************/
+
+static void
+ggv_bin_read_v2(QFile& file)
+{
+  QByteArray buf;
+  QString track_name;
+  QString waypt_name;
+  route_head* ggv_bin_track;
+  Waypoint* wpt;
+  double lon, lat;
+  quint16 header_len;
+  quint16 entry_type;
+  quint16 entry_subtype;
+  quint16 line_points;
+  quint64 entry_pos;
+
+  // header length is usually either 0x90 or 0x00
+  header_len = ggv_bin_read16(file, "map name len");
+  if (header_len > 0) {
+    ggv_bin_read_bytes(file, buf, header_len, "map name");
+    buf.remove(0,4);
+    buf.append('\0');
+    if (global_opts.debug_level > 1)
+      qDebug() << "ovl: name =" << buf.constData();
+  }
+
+  while (!file.atEnd()) {
+    track_name.clear();
+
+    if (global_opts.debug_level > 1)
+      qDebug("------------------------------------ 0x%llx", file.pos());
+
+    entry_pos = file.pos();
+    entry_type = ggv_bin_read16(file, "entry type");
+    ggv_bin_read16(file, "entry group");
+    ggv_bin_read16(file, "entry zoom");
+    entry_subtype = ggv_bin_read16(file, "entry subtype");
+
+    switch (entry_subtype) {
+    case 0x01:
+      // no data following
+      break;
+    case 0x10:
+      // text with 32 bit length field follows
+      ggv_bin_read_text32(file, buf, "text len");
+      track_name = QString::fromLatin1(buf.constData()).simplified();
+      break;
+    default:
+      fatal(MYNAME ": Unknown subtype (%hu)\n", entry_subtype);
+    }
+
+    switch (entry_type) {
+    case 0x02:
+      // text
+      ggv_bin_read16(file, "text color");
+      ggv_bin_read16(file, "text size");
+      ggv_bin_read16(file, "text trans");
+      ggv_bin_read16(file, "text font");
+      ggv_bin_read16(file, "text angle");
+      lon = ggv_bin_read_double(file, "text lon");
+      lat = ggv_bin_read_double(file, "text lat");
+      ggv_bin_read_text16(file, buf, "text label");
+      waypt_name = QString::fromLatin1(buf.constData()).simplified();
+      wpt = new Waypoint;
+      wpt->longitude = lon;
+      wpt->latitude = lat;
+      wpt->description = waypt_name;
+      waypt_add(wpt);
+      break;
+    case 0x03:
+      // line
+    case 0x04:
+      // area
+      ggv_bin_read16(file, "line color");
+      ggv_bin_read16(file, "line width");
+      ggv_bin_read16(file, "line type");
+      line_points = ggv_bin_read16(file, "line points");
+      ggv_bin_track = route_head_alloc();
+      track_add_head(ggv_bin_track);
+      if (! track_name.isEmpty())
+        ggv_bin_track->rte_name = track_name;
+
+      for (int i = 1; i <= line_points; i++) {
+        lon = ggv_bin_read_double(file, "line lon");
+        lat = ggv_bin_read_double(file, "line lat");
+        wpt = new Waypoint;
+        wpt->longitude = lon;
+        wpt->latitude = lat;
+        track_add_wpt(ggv_bin_track, wpt);
+      }
+      break;
+    case 0x05:
+      // rectangle
+    case 0x06:
+      // circle
+    case 0x07:
+      // triangle
+      ggv_bin_read16(file, "geom color");
+      ggv_bin_read16(file, "geom prop1");
+      ggv_bin_read16(file, "geom prop2");
+      ggv_bin_read16(file, "geom angle");
+      ggv_bin_read16(file, "geom stroke");
+      ggv_bin_read16(file, "geom area");
+      ggv_bin_read_double(file, "geom lon");
+      ggv_bin_read_double(file, "geom lat");
+      break;
+    case 0x09:
+      ggv_bin_read16(file, "bmp color");
+      ggv_bin_read16(file, "bmp prop1");
+      ggv_bin_read16(file, "bmp prop2");
+      ggv_bin_read16(file, "bmp prop3");
+      ggv_bin_read_double(file, "bmp lon");
+      ggv_bin_read_double(file, "bmp lat");
+      ggv_bin_read_text32(file, buf, "bmp data");
+      break;
+    default:
+      fatal(MYNAME ": Unknown entry type (0x%hx, pos=0x%llx) \n", entry_type, entry_pos);
+    }
+  }
+}
+
+/***************************************************************************
+ *           OVL Version 3.0 and 4.0                                       *
+ ***************************************************************************/
+
+static void
+ggv_bin_read_v34_header(QFile& file, quint32& number_labels, quint32 &number_records)
+{
+  QByteArray buf;
+  quint16 header_len;
+    
+  ggv_bin_read_bytes(file, buf, 8, "unknown");
+  number_labels = ggv_bin_read32(file, "num labels");
+  number_records = ggv_bin_read32(file, "num records");
+  ggv_bin_read_text16(file, buf, "text label");
+  ggv_bin_read16(file, "unknown");
+  ggv_bin_read16(file, "unknown");
+  // 8 bytes ending with 1E 00, contains len of header block
+  ggv_bin_read16(file, "unknown");
+  header_len = ggv_bin_read16(file, "header len");
+  ggv_bin_read16(file, "unknown");
+  ggv_bin_read16(file, "unknown");
+  if (header_len > 0) {
+    ggv_bin_read_bytes(file, buf, header_len, "map name");
+    buf.remove(0,4);
+    buf.append('\0');
+    if (global_opts.debug_level > 1)
+      qDebug() << "ovl: name =" << buf.constData();
+  }
+}
+
+static void
+ggv_bin_read_v34_label(QFile& file)
+{
+  QByteArray buf;
+
+  if (global_opts.debug_level > 1)
+    qDebug("------------------------------------ 0x%llx", file.pos());
+  ggv_bin_read_bytes(file, buf, 0x08, "label header");
+  ggv_bin_read_bytes(file, buf, 0x14, "label number");
+  ggv_bin_read_text16(file, buf, "label text");
+  ggv_bin_read16(file, "label flag1");
+  ggv_bin_read16(file, "label flag2");
+}
+
+static QString
+ggv_bin_read_v34_common(QFile& file)
+{
+  QByteArray buf;
+  QString res;
+  quint16 type1;
+  quint16 type2;
+  
+  ggv_bin_read16(file, "entry group");
+  ggv_bin_read16(file, "entry prop2");
+  ggv_bin_read16(file, "entry prop3");
+  ggv_bin_read16(file, "entry prop4");
+  ggv_bin_read16(file, "entry prop5");
+  ggv_bin_read16(file, "entry prop6");
+  ggv_bin_read16(file, "entry prop7");
+  ggv_bin_read16(file, "entry prop8");
+  ggv_bin_read16(file, "entry zoom");
+  ggv_bin_read16(file, "entry prop10");
+  ggv_bin_read_text16(file, buf, "entry txt");
+  res = QString::fromLatin1(buf.constData()).simplified();
+  type1 = ggv_bin_read16(file, "entry type1");
+  if (type1 != 1) {
+    ggv_bin_read_text32(file, buf, "entry object");
+  }
+  type2 = ggv_bin_read16(file, "entry type2");
+  if (type2 != 1) {
+    ggv_bin_read_text32(file, buf, "entry object");
+  }
+  return res;
+}
+
+static void
+ggv_bin_read_v34_record(QFile& file)
+{
+  QByteArray buf;
+  QString label;
+  Waypoint *wpt;
+  route_head* ggv_bin_track;
+  quint16 entry_type;
+  quint32 bmp_len;
+  quint16 line_points;
+  double lon, lat;
+
+  if (global_opts.debug_level > 1)
+    qDebug("------------------------------------ 0x%llx", file.pos());
+
+  entry_type = ggv_bin_read16(file, "entry type");
+  label = ggv_bin_read_v34_common(file);
+
+  switch (entry_type) {
+  case 0x02:
+    // text
+    ggv_bin_read16(file, "text prop1");
+    ggv_bin_read32(file, "text prop2");
+    ggv_bin_read16(file, "text prop3");
+    ggv_bin_read32(file, "text prop4");
+    ggv_bin_read16(file, "text ltype");
+    ggv_bin_read16(file, "text angle");
+    ggv_bin_read16(file, "text size");
+    ggv_bin_read16(file, "text area");
+    lon = ggv_bin_read_double(file, "text lon");
+    lat = ggv_bin_read_double(file, "text lat");
+    ggv_bin_read_double(file, "text unk");
+    ggv_bin_read_text16(file, buf, "text label");
+    wpt = new Waypoint;
+    wpt->longitude = lon;
+    wpt->latitude = lat;
+    wpt->description = QString::fromLatin1(buf.constData()).simplified();
+    waypt_add(wpt);
+    break;
+  case 0x03:
+  case 0x04:
+    // area
+  case 0x17:
+    // line
+    ggv_bin_track = route_head_alloc();
+    track_add_head(ggv_bin_track);
+      
+    if (! label.isEmpty()) 
+      ggv_bin_track->rte_name = label;
+
+    ggv_bin_read16(file, "line prop1");
+    ggv_bin_read32(file, "line prop2");
+    ggv_bin_read16(file, "line prop3");
+    ggv_bin_read32(file, "line color");
+    ggv_bin_read16(file, "line size");
+    ggv_bin_read16(file, "line stroke");
+    line_points = ggv_bin_read16(file, "line points");
+    if (entry_type == 0x04) {
+      // found in example.ovl generated by Geogrid-Viewer 1.0
+      ggv_bin_read16(file, "line pad");
+    }
+
+    for (int i=1; i <= line_points; i++) {
+      lon = ggv_bin_read_double(file, "line lon");
+      lat = ggv_bin_read_double(file, "line lat");
+      ggv_bin_read_double(file, "line unk");
+      wpt = new Waypoint;
+      wpt->longitude = lon;
+      wpt->latitude = lat;
+      track_add_wpt(ggv_bin_track, wpt);
+    }
+    break;
+  case 0x05:
+  case 0x06:
+  case 0x07:
+    // circle
+    ggv_bin_read16(file, "circle prop1");
+    ggv_bin_read32(file, "circle prop2");
+    ggv_bin_read16(file, "circle prop3");
+    ggv_bin_read32(file, "circle color");
+    ggv_bin_read32(file, "circle prop5");
+    ggv_bin_read32(file, "circle prop6");
+    ggv_bin_read16(file, "circle ltype");
+    ggv_bin_read16(file, "circle angle");
+    ggv_bin_read16(file, "circle size");
+    ggv_bin_read16(file, "circle area");
+    ggv_bin_read_double(file, "circle lon");
+    ggv_bin_read_double(file, "circle lat");
+    ggv_bin_read_double(file, "circle unk");
+    break;
+  case 0x09:
+    // bmp
+    ggv_bin_read16(file, "bmp prop1");
+    ggv_bin_read32(file, "bmp prop2");
+    ggv_bin_read16(file, "bmp prop3");
+    ggv_bin_read32(file, "bmp prop4");
+    ggv_bin_read32(file, "bmp prop5");
+    ggv_bin_read32(file, "bmp prop6");
+    ggv_bin_read_double(file, "bmp lon");
+    ggv_bin_read_double(file, "bmp lat");
+    ggv_bin_read_double(file, "bmp unk");
+    bmp_len = ggv_bin_read32(file, "bmp len");
+    ggv_bin_read16(file, "bmp prop");
+    ggv_bin_read_bytes(file, buf, bmp_len, "bmp data");
+    break;
+  default:
+    fatal(MYNAME ": Unsupported type: %x\n", entry_type);
+  }
+}
+
+static void
+ggv_bin_read_v34(QFile& file)
+{
+  QByteArray buf;
+  QString track_name;
+  quint32 label_count;
+  quint32 record_count;
+
+  while (!file.atEnd()) {
+    ggv_bin_read_v34_header(file, label_count, record_count);
+
+    if (label_count && !file.atEnd()) {
+      if (global_opts.debug_level > 1)
+        qDebug("-----labels------------------------- 0x%llx", file.pos());
+      for (unsigned int i = 0; i < label_count; i++)
+        ggv_bin_read_v34_label(file);
+    }
+
+    if (record_count && !file.atEnd()) {
+      if (global_opts.debug_level > 1)
+        qDebug("-----records------------------------ 0x%llx", file.pos());
+      for (unsigned int i = 0; i < record_count; i++)
+        ggv_bin_read_v34_record(file);
+    }
+
+    if (!file.atEnd()) {
+      if (global_opts.debug_level > 1)
+        qDebug("------------------------------------ 0x%llx", file.pos());
+      // we just skip over the next magic bytes without checking they
+      // contain the correct string. This is consistent with what I
+      // believe GGV does
+      ggv_bin_read_bytes(file, buf, 23, "magicbytes");
+      if (global_opts.debug_level > 1)
+        qDebug() << "ovl: header = " << buf.constData();
+    }
+  }
+    
+  if (global_opts.debug_level > 1) {
+    qDebug("fpos: 0x%llx", file.pos());
+    qDebug("size: 0x%llx", file.size());
+  }
+}
+
+/***************************************************************************
+ *           global callbacks called by gpsbabel main process              *
+ ***************************************************************************/
+
+static void
+ggv_bin_read_file(QFile& file)
+{
+  QByteArray buf;
+
+  ggv_bin_read_bytes(file, buf, 0x17, "magic");
+  buf[23] = 0;
+  if (global_opts.debug_level > 1) {
+    qDebug() << "ovl: header =" << buf.constData();
+  }
+
+  if (buf.startsWith("DOMGVCRD Ovlfile V2.0")) {
+    ggv_bin_read_v2(file);
+  } else if (buf.startsWith("DOMGVCRD Ovlfile V3.0")) {
+    ggv_bin_read_v34(file);
+  } else if (buf.startsWith("DOMGVCRD Ovlfile V4.0")) {
+    ggv_bin_read_v34(file);
+  } else {
+    fatal(MYNAME ": Unsupported file format\n");
+  }
+}
+
+static void
+ggv_bin_read_init(const char* fname)
+{
+  read_fname = QString::fromUtf8(fname);
+}
+
+static void
+ggv_bin_read_deinit(void)
+{
+}
+
+static void
+ggv_bin_read(void)
+{
+  QFile file(read_fname);
+
+  if (!file.open(QIODevice::ReadOnly)) {
+    fatal(MYNAME ": Error opening file %s\n", read_fname.toStdString().c_str());
+  }
+
+  ggv_bin_read_file(file);
+  file.close();
+}
+
+ff_vecs_t ggv_bin_vecs = {
+  ff_type_file,
+  {
+    ff_cap_none,  // waypoints
+    ff_cap_read,  // tracks
+    ff_cap_none   // routes
+  },
+  ggv_bin_read_init,    // rd_init
+  NULL,                 // wr_init
+  ggv_bin_read_deinit,  // rd_deinit
+  NULL,                 // wr_deinit
+  ggv_bin_read,         // read
+  NULL,                 // write
+  NULL,                 // exit
+  NULL,                 //args
+  CET_CHARSET_ASCII, 0  //encode,fixed_encode
+  //NULL                //name dynamic/internal?
+};
diff --git a/reference/ggv_bin-sample-v3.gpx b/reference/ggv_bin-sample-v3.gpx
new file mode 100644 (file)
index 0000000..fc0c970
--- /dev/null
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gpx version="1.0" creator="GPSBabel - http://www.gpsbabel.org" xmlns="http://www.topografix.com/GPX/1/0">
+  <time>1970-01-01T00:00:00Z</time>
+  <bounds minlat="51.390787309" minlon="7.634582135" maxlat="51.416622730" maxlon="7.670407825"/>
+  <wpt lat="51.400591976" lon="7.655250113">
+    <name>Beispiel-Text</name>
+    <cmt>Beispiel-Text</cmt>
+    <desc>Beispiel-Text</desc>
+  </wpt>
+  <trk>
+    <name>Linie</name>
+    <trkseg>
+      <trkpt lat="51.416622730" lon="7.634582135"/>
+      <trkpt lat="51.413106821" lon="7.637561773"/>
+      <trkpt lat="51.409758874" lon="7.639529832"/>
+      <trkpt lat="51.407628129" lon="7.641820840"/>
+      <trkpt lat="51.406255307" lon="7.643586185"/>
+      <trkpt lat="51.403222581" lon="7.645616042"/>
+      <trkpt lat="51.399781311" lon="7.647298422"/>
+      <trkpt lat="51.398632218" lon="7.648984855"/>
+      <trkpt lat="51.397982280" lon="7.651087602"/>
+      <trkpt lat="51.398325694" lon="7.653592205"/>
+      <trkpt lat="51.398944395" lon="7.656591723"/>
+      <trkpt lat="51.398664437" lon="7.659617592"/>
+      <trkpt lat="51.396813518" lon="7.662832934"/>
+      <trkpt lat="51.393519865" lon="7.665658933"/>
+      <trkpt lat="51.390787309" lon="7.670407825"/>
+    </trkseg>
+  </trk>
+  <trk>
+    <name>Fläche</name>
+    <trkseg>
+      <trkpt lat="51.402506568" lon="7.653684988"/>
+      <trkpt lat="51.403988399" lon="7.653569612"/>
+      <trkpt lat="51.406267829" lon="7.656377105"/>
+      <trkpt lat="51.404428210" lon="7.656646600"/>
+      <trkpt lat="51.403620311" lon="7.656742131"/>
+      <trkpt lat="51.402285607" lon="7.657930916"/>
+      <trkpt lat="51.401834671" lon="7.657800408"/>
+      <trkpt lat="51.402486377" lon="7.655841237"/>
+      <trkpt lat="51.402417535" lon="7.653759457"/>
+      <trkpt lat="51.402506568" lon="7.653684988"/>
+    </trkseg>
+  </trk>
+</gpx>
diff --git a/reference/ggv_bin-sample-v3.ovl b/reference/ggv_bin-sample-v3.ovl
new file mode 100644 (file)
index 0000000..60cf3f7
Binary files /dev/null and b/reference/ggv_bin-sample-v3.ovl differ
diff --git a/testo.d/ggv_bin.test b/testo.d/ggv_bin.test
new file mode 100644 (file)
index 0000000..9351091
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# Geogrid-Viewer binary overlay files
+#
+rm -f ${TMPDIR}/ggv_bin*
+gpsbabel -i ggv_bin -f ${REFERENCE}/ggv_bin-sample-v3.ovl -o gpx -F ${TMPDIR}/ggv_bin-sample-v3.gpx
+bincompare ${REFERENCE}/ggv_bin-sample-v3.gpx ${TMPDIR}/ggv_bin-sample-v3.gpx
diff --git a/vecs.cc b/vecs.cc
index 3bb2d76e1d75a1db6ba3c77c5af5d616b386be83..bd76f07693b8a211f8c60a52ae43a1e0d7a9e051 100644 (file)
--- a/vecs.cc
+++ b/vecs.cc
@@ -132,6 +132,7 @@ extern ff_vecs_t raymarine_vecs;
 extern ff_vecs_t alanwpr_vecs;
 extern ff_vecs_t alantrl_vecs;
 extern ff_vecs_t vitovtt_vecs;
+extern ff_vecs_t ggv_bin_vecs;
 extern ff_vecs_t ggv_log_vecs;
 extern ff_vecs_t g7towin_vecs;
 extern ff_vecs_t garmin_gpi_vecs;
@@ -1093,6 +1094,13 @@ vecs_t vec_list[] = {
     "trc",
     NULL,
   },
+  {
+    &ggv_bin_vecs,
+    "ggv_bin",
+    "Geogrid-Viewer binary overlay file (.ovl)",
+    "ovl",
+    NULL,
+  },
 #endif // MAXIMAL_ENABLED
   {
     NULL,
diff --git a/xmldoc/formats/ggv_bin.xml b/xmldoc/formats/ggv_bin.xml
new file mode 100644 (file)
index 0000000..4d83aab
--- /dev/null
@@ -0,0 +1,22 @@
+<para>
+   Binary overlay files (.ovl) used by
+   <productname>Geogrid</productname>-Viewer.
+</para>
+<para>
+   Geogrid-Viewer is part of several
+   <ulink url="https://de.wikipedia.org/wiki/Top50">Top50</ulink>
+   map products available in Germany and Austria.
+</para>
+<para>
+   This module supports binary overlay file format version 2.0, 3.0
+   and 4.0. Those files can be identified by looking at the first
+   bytes:
+</para>
+<programlisting>
+hexdump -C example.ovl | head -n 2
+00000000  44 4f 4d 47 56 43 52 44  20 4f 76 6c 66 69 6c 65  |DOMGVCRD Ovlfile|
+00000010  20 56 33 2e 30 3a 00 00  00 08 00 00 00 1e 00 00  | V3.0:..........|
+</programlisting>
+<para>
+   The current file version 5.0 is not supported.
+</para>